package com.zlc.workflow.core.instance.cmd;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.common.constant.WorkflowConstant;
import com.zlc.common.common.exception.WorkflowException;
import com.zlc.common.core.process.model.vo.StoneNodeApproveConfigVo;
import com.zlc.common.core.process.model.vo.StoneNodeApproveUserConfigVo;
import com.zlc.common.core.process.model.vo.StoneProcessNodeVo;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.model.vo.StoneTaskVo;
import com.zlc.common.core.process.service.StoneNodeApproveConfigService;
import com.zlc.common.core.process.service.StoneNodeApproveUserConfigService;
import com.zlc.common.core.process.service.StoneProcessNodeService;
import com.zlc.common.core.process.service.StoneProcessService;
import com.zlc.common.core.process.service.StoneTaskService;
import com.zlc.common.core.user.model.entity.MayanPlatUser;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.workflow.constant.NodeType;
import com.zlc.workflow.core.instance.model.NextNode;
import com.zlc.workflow.core.instance.model.NodeApproveUser;
import com.zlc.workflow.core.instance.model.QueryNextTaskParam;
import com.zlc.workflow.core.instance.model.WorkflowTaskNode;
import com.zlc.workflow.core.instance.service.WorkflowService;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.Path;
import com.zlc.workflow.flow.node.TaskNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import static com.zlc.common.common.constant.WorkflowConstant.APPROVE_TYPE_MORE;
import static com.zlc.common.common.constant.WorkflowConstant.APPROVE_TYPE_MORE_SELECT;
import static com.zlc.common.common.constant.WorkflowConstant.APPROVE_TYPE_ONLY;
import static com.zlc.common.common.constant.WorkflowConstant.APPROVE_TYPE_ONLY_FIRST_ONE;
import static com.zlc.common.common.constant.WorkflowConstant.FORK_RULE_ONE_MORE;
import static com.zlc.common.common.constant.WorkflowConstant.SAME_APPROVE_USER_CONTINUE;

/**
 * 获取后续节点及审批人信息
 */
public class NextTaskNodeCmd extends AbstractCmd<WorkflowTaskNode> {

    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;
    @Autowired
    private StoneNodeApproveConfigService stoneNodeApproveConfigService;
    @Autowired
    private StoneNodeApproveUserConfigService stoneNodeApproveUserConfigService;
    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private WorkflowService workflowService;

    //流程所有节点配置
    private final List<StoneNodeApproveConfigVo> approveConfigVos;
    private final List<StoneProcessNodeVo> stoneProcessNodeVos;

    private final QueryNextTaskParam workflowParam;

    //下一任务节点是否跳过
    private boolean nextTaskContinue;
    //当前操作人
    private MayanPlatUserVo nowUser;
    private Map<String,Object> param = new HashMap<>();

    private final List<NextNode> nexts = new ArrayList<>();

    private final WorkflowTaskNode result = new WorkflowTaskNode();

    private NodeModel joinModel;

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     */
    public NextTaskNodeCmd(Object params) {
        super(params);
        this.workflowParam = (QueryNextTaskParam)params;
        AssertUtils.isTrue(!ObjectUtils.isEmpty(workflowParam.getAccount()),"操作人不可为空");
        this.nowUser = mayanPlatUserService.queryByUserAccount(workflowParam.getAccount());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.nowUser),"操作人不存在");
        this.param = this.workflowParam.getFormData() == null ? new HashMap<>() : this.workflowParam.getFormData();
        this.approveConfigVos = stoneNodeApproveConfigService.getByProcessId(this.workflowParam.getProcessId());
        this.stoneProcessNodeVos = stoneProcessNodeService.getByProcessId(this.workflowParam.getProcessId());
    }


    @Override
    public WorkflowTaskNode execute() {
        return nextTask();
    }

    /**
     * 1.通过processId获取流程模型
     * 2、如果taskId为空，查询模型第一个任务节点，并获取后续节点，包括分支节点和任务节点。如果是分支节点则继续往后获取任务节点
     *    获取后续节点的节点配置，并设置对应值到返回值。
     *
     */
    public WorkflowTaskNode nextTask(){
        String processId = this.workflowParam.getProcessId();
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processVo),"无效的流程-"+processId);
        //流程内容
        String content = NodeUtils.getProcessContent(processVo.getContent());
        String processInstanceId = this.workflowParam.getProcessInstanceId();
        if (ObjectUtils.isEmpty(processInstanceId)) {
            //首次发起
            TaskNode firstTask = NodeUtils.getFirstTask(NodeUtils.getProcessContent(processVo.getContent()), processId);
            result.setNodeId(firstTask.getNodeId());
            result.setNodeName(firstTask.getNodeName());
            result.setSelectMoreFlag(Boolean.FALSE);
            result.setSelectFlag(Boolean.FALSE);

            List<NextNode> nextNodes = getNextNodes(content,processId, result.getNodeId());
            result.setNextNodes(nextNodes);
        }else{
            //不是首次发起
            String taskId = this.workflowParam.getTaskId();
            AssertUtils.isTrue(!ObjectUtils.isEmpty(taskId),"缺少任务ID");
            StoneTaskVo taskVo = stoneTaskService.getByTaskId(taskId);
            AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"任务ID错误");
            if(!this.workflowParam.getAccount().equals(taskVo.getActorId())){
                throw new WorkflowException("无权限操作！");
            }
            TaskNode taskNode = NodeUtils.getTaskNode(content, processId, taskVo.getNodeId());
            result.setNodeId(taskNode.getNodeId());
            result.setNodeName(taskNode.getNodeName());
            result.setSelectMoreFlag(Boolean.FALSE);
            result.setSelectFlag(Boolean.FALSE);
            List<NextNode> nextNodes = getNextNodes(content,processId, result.getNodeId());
            result.setNextNodes(nextNodes);
        }
        return result;
    }


    private List<NextNode> getNextNodes(String content,String processId,String nodeId){
        TaskNode taskNode = NodeUtils.getTaskNode(content,processId,nodeId);
        if(ObjectUtils.isEmpty(taskNode)){
            return new ArrayList<>();
        }
        nextTask(taskNode);
        //如果没有查询到或许任务节点，说明节点在聚合节点，继续往后查
        if(this.joinModel != null && ObjectUtils.isEmpty(this.nexts)){
            nextTask(this.joinModel);
        }
        return nexts;
    }

    private void nextTask(NodeModel taskNode){
        List<NodeModel> nextNodes = taskNode.next();
        for(NodeModel nodeModel : nextNodes){
            NodeType nodeType = nodeModel.nodeType();
            NextNode nextNode = new NextNode();
            nextNode.setNodeId(nodeModel.nodeId());
            nextNode.setNodeName(nodeModel.nodeName());
            nextNode.setNodeType(nodeType.value());
            nextNode.setApproveSelectFlag(Boolean.FALSE);
            initNextNode(nextNode,nodeModel);
        }
    }

    private void initNextNode(NextNode nextNode,NodeModel nodeModel){
        NodeType nodeType = nodeModel.nodeType();
        if(NodeType.TASK.equals(nodeType)){
            //任务
            loadTaskApproveConfig(nextNode,nodeModel);
        }else if(NodeType.AUTO.equals(nodeType)){
            //自动处理
            this.nextTaskContinue = true;
        }else if(NodeType.WAIT.equals(nodeType)){
            //等待节点,不需要审批人信息，直接返回
            return;
        }else if(NodeType.DISC.equals(nodeType)){
            //决策节点
            List<Path> pathList = nodeModel.path();
            for(Path path : pathList){
                String nodeId = path.getNodeId();
                StoneProcessNodeVo pathNode = stoneProcessNodeVos.stream().filter(s->s.getNodeId().equals(nodeId)).findFirst().orElse(null);
                if(!ObjectUtils.isEmpty(pathNode) && !ObjectUtils.isEmpty(pathNode.getDiscRules())){
                    String discRules = pathNode.getDiscRules();
                    Boolean discBool = NodeUtils.expressBool(this.param, discRules);
                    if(Boolean.TRUE.equals(discBool)){
                        this.nextTaskContinue = false;
                        initNextNode(nextNode,path.getNextNode());
                    }
                }
            }
        } else if (NodeType.FORK.equals(nodeType)) {
            //分支节点
            StoneProcessNodeVo nodeVo = stoneProcessNodeVos.stream().filter(s -> s.getNodeId().equals(nodeModel.nodeId())).findFirst().orElse(null);
            if(!ObjectUtils.isEmpty(nodeVo)){
                String forkRules = nodeVo.getForkRules();
                this.result.setSelectFlag(WorkflowConstant.FORK_RULE_ONE.equals(forkRules) || FORK_RULE_ONE_MORE.equals(forkRules));
                this.result.setSelectMoreFlag(FORK_RULE_ONE_MORE.equals(forkRules));
                List<NodeModel> nexts = nodeModel.next();
                List<Path> paths = nodeModel.path();
                for(NodeModel next : nexts){
                    NextNode newNext = BeanCopyUtils.copy(nextNode,NextNode.class);
                    newNext.setNodeId(next.nodeId());
                    //路线
                    Path path = paths.stream().filter(p -> next.nodeId().equals(p.getTo())).findFirst().orElse(null);
                    newNext.setPathNodeId(path == null ? null : path.getNodeId());
                    newNext.setNodeName(next.nodeName());
                    newNext.setNodeType(next.nodeType().value());
                    newNext.setApproveUsers(new LinkedList<>());
                    this.nextTaskContinue = false;
                    initNextNode(newNext,next);
                }
            }
        }else if(NodeType.JOIN.equals(nodeType)){
            this.nextTaskContinue = false;
            this.joinModel = nodeModel;
        }else if(NodeType.END.equals(nodeType)){
            //结束节点
            this.nexts.add(nextNode);
            return;
        }
        if(Boolean.TRUE.equals(this.nextTaskContinue)){
            //如果需要跳过，则继续递归
            this.nextTaskContinue = false;
            List<NodeModel> nexts = nodeModel.next();
            nextNode.setApproveUsers(new LinkedList<>());
            for(NodeModel next : nexts){
                initNextNode(nextNode,next);
            }
        }
    }

    /**
     * 校验和设置审批人
     *
     */
    private void checkAndSetApproveUser(List<StoneNodeApproveUserConfigVo> userConfigVos,NextNode nextNode,StoneNodeApproveConfigVo approveConfigVo){
        //审批类型
        String approveType = approveConfigVo.getApproveType();
        //审批人不存在时，处理方式
        String noUserApprove = approveConfigVo.getNoUserApprove();
        //是否按条件设置审批人
        Boolean conditionFlag = approveConfigVo.getConditionFlag();
        //条件
        String condition = approveConfigVo.getCondition();
        boolean conditionBool = false;
        if(Boolean.TRUE.equals(conditionFlag)){
            conditionBool = NodeUtils.expressBool(param, condition);
        }
        //和上一个操作人相同时，处理方式
        String sameLastApprove = approveConfigVo.getSameLastApprove();
        if(ObjectUtils.isEmpty(userConfigVos)){
            if(WorkflowConstant.NO_APPROVE_USER_ERROR.equals(noUserApprove)){
                //不存在，报错
                throw new WorkflowException("无法获取下一步审批人，节点->"+nextNode.getNodeName());
            }else{
                //不存在跳过
                this.nextTaskContinue = true;
                return;
            }
        }
        boolean exit = false;
        for(StoneNodeApproveUserConfigVo configVo : userConfigVos){
            //用户配置类型
            String approveUser = configVo.getApproveUser();
            if(ObjectUtils.isEmpty(approveUser)){
                continue;
            }
            if(Boolean.TRUE.equals(conditionFlag) && (ObjectUtils.isEmpty(configVo.getConditionRes())
                           || !configVo.getConditionRes().equalsIgnoreCase(String.valueOf(conditionBool)))){
                //如果是条件设置审批人,不符合条件的，不用设置
                continue;
            }
            List<MayanPlatUserVo> userVoList = workflowService.getApprovalUserByConfig(configVo, workflowParam.getProcessInstanceId(), workflowParam.getFormData());
            if(!ObjectUtils.isEmpty(userVoList)){
                exit = true;
                nextNode.getApproveUsers().addAll(nodeApproveUser(userVoList));
            }
        }
        if(Boolean.FALSE.equals(exit) || ObjectUtils.isEmpty(nextNode.getApproveUsers())){
            if(WorkflowConstant.NO_APPROVE_USER_ERROR.equals(noUserApprove)){
                //不存在，报错
                throw new WorkflowException("无法获取下一步审批人，节点->"+nextNode.getNodeName());
            }else{
                this.nextTaskContinue = true;
                return;
            }
        }
        //当审批类型为单个审批，和上一步操作人相同时
        if(SAME_APPROVE_USER_CONTINUE.equals(sameLastApprove) && APPROVE_TYPE_ONLY.equals(approveType)
                && !ObjectUtils.isEmpty(nextNode.getApproveUsers())){
            LinkedList<NodeApproveUser> approveUsers = nextNode.getApproveUsers();
            if(workflowParam.getAccount().equals(approveUsers.get(0).getAccount())){
                this.nextTaskContinue = true;
            }
        }
    }

    private void loadTaskApproveConfig(NextNode nextNode,NodeModel nodeModel){
        StoneNodeApproveConfigVo approveConfigVo = approveConfigVos.stream()
                .filter(a -> a.getNodeId().equals(nodeModel.nodeId())).findFirst().orElse(null);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(approveConfigVo),"节点"+nextNode.getNodeId()+"缺少配置！");
        //审批人
        List<StoneNodeApproveUserConfigVo> userConfigVos = stoneNodeApproveUserConfigService.getByConfigId(approveConfigVo.getConfigId());
        if(!ObjectUtils.isEmpty(nextNode)){
            //审批类型
            String approveType = approveConfigVo.getApproveType();
            //校验和设置审批人
            checkAndSetApproveUser(userConfigVos,nextNode,approveConfigVo);
            if(Boolean.TRUE.equals(this.nextTaskContinue)){
                return;
            }
            nextNode.setApproveSelectFlag(Boolean.FALSE);
            if(APPROVE_TYPE_ONLY.equals(approveType)){
                //单人审批
                String onlyApproveType = approveConfigVo.getOnlyApprove();
                if(APPROVE_TYPE_ONLY_FIRST_ONE.equals(onlyApproveType) || ObjectUtils.isEmpty(onlyApproveType)){
                    //取第一个
                    nextNode.setApproveUsers(new LinkedList<>(Collections.singleton(nextNode.getApproveUsers().get(0))));
                }else{
                    nextNode.setApproveSelectFlag(Boolean.TRUE);
                }
            }else if(APPROVE_TYPE_MORE.equals(approveType)){
                //多人审批
                String moreApproveType = approveConfigVo.getMoreApprove();
                if(APPROVE_TYPE_MORE_SELECT.equals(moreApproveType)){
                    nextNode.setApproveSelectFlag(Boolean.TRUE);
                }
            }
            nextNode.setNodeId(nodeModel.nodeId());
            nextNode.setNodeName(nodeModel.nodeName());
            nextNode.setNodeType(nodeModel.nodeType().getValue());
            this.nexts.add(nextNode);
        }
    }


    private NodeApproveUser nodeApproveUser(MayanPlatUserVo userVo){
        NodeApproveUser user = new NodeApproveUser();
        user.setAccount(userVo.getUserAccount());
        user.setUsername(userVo.getUsername());
        return user;
    }

    private List<NodeApproveUser> nodeApproveUser(List<MayanPlatUserVo> userVos){
        List<NodeApproveUser> approveUsers = new ArrayList<>();
        for(MayanPlatUserVo userVo : userVos){
            NodeApproveUser user = new NodeApproveUser();
            user.setAccount(userVo.getUserAccount());
            user.setUsername(userVo.getUsername());
            approveUsers.add(user);
        }
        return approveUsers;
    }


}
